﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Microsoft.Build.Tasks;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace Microsoft.SPOT.Tasks.Internal
{
    public class GenerateCLRDefines : Task
    {
        string defines = "";
        string outfile = "";

        Dictionary<string, string> m_oldPropMap;
        Dictionary<string, string> m_newPropMap;

        public override bool Execute()
        {
            try
            {
                bool fIncompat = false;
                m_oldPropMap = new Dictionary<string, string>();
                m_newPropMap = new Dictionary<string, string>();
                string path = Path.GetDirectoryName(outfile);

                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }

                if (File.Exists(outfile))
                {
                    Regex parse = new Regex(@"#define\s+(\w+)\s+?(\w*)");
                    using (TextReader tr = File.OpenText(outfile))
                    {
                        string line;
                        while(null != (line = tr.ReadLine()))
                        {
                            Match m = parse.Match(line);

                            if (m.Success)
                            {
                                if (m.Groups.Count == 3)
                                {
                                    m_oldPropMap[m.Groups[1].Value] = m.Groups[2].Value;
                                }
                                else if(m.Groups.Count == 2)
                                {
                                    m_oldPropMap[m.Groups[1].Value] = "";
                                }
                                else
                                {
                                    this.Log.LogWarning("The old CLR defines header ({0}) was incorrectly formatted: {1}", outfile, line);
                                }
                            }
                        }
                    }
                }

                string[] defs = defines.Split(';');

                for (int i = 0; i < defs.Length; i++)
                {
                    string[] parts = defs[i].Split(new char[]{'=', ' '}, StringSplitOptions.RemoveEmptyEntries);

                    if (parts.Length > 0)
                    {
                        if (parts.Length > 1)
                        {
                            m_newPropMap[parts[0]] = parts[1].Trim();
                        }
                        else
                        {
                            m_newPropMap[parts[0]] = "";
                        }

                        if (m_oldPropMap.ContainsKey(parts[0]))
                        {
                            if (m_oldPropMap[parts[0]] != m_newPropMap[parts[0]])
                            {
                                fIncompat = true;
                            }
                            m_oldPropMap.Remove(parts[0]);
                        }
                        else
                        {
                            fIncompat = true;
                        }
                    }
                }

                if (m_oldPropMap.Count > 0)
                {
                    fIncompat = true;
                }

                if (fIncompat || !File.Exists(outfile))
                {
                    if(File.Exists(outfile))
                    {
                        File.Delete(outfile);
                    }

                    using (TextWriter tw = File.CreateText(outfile))
                    {
                        tw.WriteLine(
@"//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by the build
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------");

                        tw.WriteLine();


                        foreach(string key in m_newPropMap.Keys)
                        {
                            tw.WriteLine("#define {0} {1}", key, m_newPropMap[key]);
                        }
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                this.Log.LogErrorFromException(e);
                return false;
            }
        }

        [Required]
        public string CLR_Defines
        {
            get { return defines; }
            set { defines = value; }
        }

        [Required]
        public string OutputFile
        {
            get { return outfile; }
            set { outfile = value; }
        }

    }
}
